package com.example.springdemo.demos.until;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.legacy.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.HashMap;
import java.util.Map;


/**
 * @author Derek Sin
 * @since 2023/7/20 10:21
 * @Description 加密相关的静态代码
 */
@Slf4j
public class EncryptUtils {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    private static final String ENCODING = "UTF-8";
//    private static final String KEY = "test";

    public static final String ALGORITHM_NAME_SM4 = "SM4";
    public static final String ALGORITHM_NAME_SM2 = "SM2";
    // 加密算法/分组加密模式/分组填充方式
    // PKCS5Padding-以8个字节为一组进行分组加密
    // 定义分组加密模式使用：PKCS5Padding
    public static final String ALGORITHM_NAME_ECB_PADDING7 = "SM4/ECB/PKCS5Padding";
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
//    public static final String ALGORITHM_NAME_ECB_NO_PADDING = "SM4/ECB/NoPadding";
    // 64-16位16进制；128-32位16进制；256-64位16进制
    public static final int DEFAULT_KEY_SIZE = 128;

    public String tripleDes(String input, String key, String vector) {
        if (StringUtils.isNotBlank(input)) {
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            if (keyBytes.length < 24) {
                int i = 0;
                byte[] tmp = new byte[24];
                for (byte kb : keyBytes) {
                    tmp[i++] = kb;
                }
                keyBytes = tmp;
            }
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "DESede");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(vector.getBytes(StandardCharsets.UTF_8));
            try {
                Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS7Padding");
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
                byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);
                byte[] finalBytes = cipher.doFinal(inputBytes);
                return Base64.encodeBase64String(finalBytes).replaceAll("\r", "").replaceAll("\n", "");
            } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
                     InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
                log.error(e.getMessage());
            }
        }
        return StringUtils.EMPTY;
    }
    /**
     * sm2加密
     *
     * @param input 输入的要编码的字符串
     * @return 返回16进制的加密字符串
     * @throws Exception
     *
     */
//    public static String encryptInSM2(String input, String publicKey) throws Exception {
//        log.info("获取公钥---" + publicKey);
//        byte[] publicKeyBytes = Hex.decode(publicKey);
//        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
//        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
//        PublicKey publicKey_gen = keyFactory.generatePublic(keySpec);
//
//        // 使用SM2加密
//        Cipher cipher = Cipher.getInstance("SM2", "BC");
//        cipher.init(Cipher.ENCRYPT_MODE, publicKey_gen);
//        byte[] res = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
//        return new String(res);
//    }

    /**
     * sm3加密
     *
     * @param input 输入的要编码的字符串
     * @return 返回16进制的加密字符串
     * @throws Exception
     *
     */
    public static String encryptInSM3(String input) throws Exception {
        byte[] md = new byte[32];
        byte[] msg1 = input.getBytes();
        SM3Digest sm3 = new SM3Digest();
        sm3.update(msg1, 0, msg1.length);
        sm3.doFinal(md, 0);
        String s = new String(Hex.encode(md));
        return s.toUpperCase();
    }


    /**
     * sm4加密
     *
     * @param input 输入的要编码的字符串
     * @return 返回16进制的加密字符串
     * @throws Exception
     * @explain 加密模式：ECB
     * 密文长度不固定，会随着被加密字符串长度的变化而变化,结果默认大写
     */
    public static String encryptInSM4(String input, String KEY) throws Exception {
        String cipherText = "";
        String charset = ENCODING;
        // 16进制字符串-->byte[]
        byte[] keyData = ByteUtils.fromHexString(KEY);
//        byte[] keyData = KEY.getBytes(ENCODING);
//        byte[] keyData = generateKey(KEY);
        charset = charset.trim();
        if (charset.length() <= 0) {
            charset = ENCODING;
        }


        // String-->byte[]
        byte[] srcData = input.getBytes(charset);
        // 加密后的数组
        byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
        // byte[]-->hexString
        cipherText = ByteUtils.toHexString(cipherArray).toUpperCase(); // 全大写
//        cipherText = Base64.encodeBase64String(cipherArray);
        return cipherText;
    }

    public static String encryptInSM4(String input, String KEY, boolean upperCaseFormat) throws Exception {
        String cipherText = "";
        String charset = ENCODING;
        // 16进制字符串-->byte[]
        byte[] keyData = ByteUtils.fromHexString(KEY);
//        byte[] keyData = KEY.getBytes(ENCODING);
//        byte[] keyData = generateKey(KEY);
        charset = charset.trim();
        if (charset.length() <= 0) {
            charset = ENCODING;
        }


        // String-->byte[]
        byte[] srcData = input.getBytes(charset);
        // 加密后的数组
        byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
        // byte[]-->hexString
        cipherText = ByteUtils.toHexString(cipherArray); // 全大写
        if (upperCaseFormat) {
            cipherText = cipherText.toUpperCase();
        } else {
            cipherText = cipherText.toLowerCase();
        }
//        cipherText = Base64.encodeBase64String(cipherArray);
        return cipherText;
    }


    /**
     * 加密模式之Ecb
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     * @explain
     */
    private static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 生成ECB暗号
     *
     * @param algorithmName 算法名称
     * @param mode          模式
     * @param key
     * @return
     * @throws Exception
     * @explain ECB模式（电子密码本模式：Electronic codebook）
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
        log.info("模式--" + algorithmName);
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME_SM4);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    private static byte[] generateKey(String keyString) throws Exception {
        // Use SHA-256 to hash the string and then take first 128 bits (16 bytes)
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(keyString.getBytes(StandardCharsets.UTF_8));
        byte[] key = new byte[16];
        System.arraycopy(hash, 0, key, 0, 16);
        return key;
    }


    public static void main(String[] args) throws Exception {
//        String XINGHE_KEY = "129773ea1d8f4422a4695d071e8e810d";
        String XINGHE_KEY = "2631d19e7c8540dbb49412e6f11c318c";
        Map<String,Object> m = new HashMap<>();
        /**
         *{ "WARD_ID":" 6903",
         *   "ROLE":"M1",
         *   "USER_ID":"YJC",
         *   "USER_NAME":"杏和杨嘉辰"}
         */
        m.put("WARD_ID","6903");
        m.put("ROLE","M1");
        m.put("USER_ID","YJC");
        m.put("USER_NAME","杏和杨嘉辰");


        String encryptUtils = EncryptUtils.encryptInSM4(JSON.toJSONString(m), XINGHE_KEY);
        System.out.println("加密后："+encryptUtils);
        String assert_res = "a9e7eaf9e87eccd34a6d36ded6d637c382a02a14923b3e8792dbcfeaeb552f26af93c61f06c50f4f62a47726f62afabfd499b6539f1ee41bf5327a4722221e3f06d4acca560e73cc0b23c4fde7d877655f330663340bb0cc46967c46f737b164";
        System.out.println(assert_res.equals(encryptUtils));

    }
}
